open Unix

open Common
open Io
open Ftp_session
open Ftp_core
open Error
open Cmd

exception Exit_toplevel

(******************************************************************************)

let get_argvs cmdline =
  let argvs = ref [] in
  let arg = ref "" in
  let previous_escape = ref false in
  let previous_blank = ref false in
  for i = 0 to (String.length cmdline)-1 do
    let c = cmdline.[i] in
    match c with
    | '\\' ->
        if !previous_escape then
          invalid_arg "encounter unnecessary \\"
        else
          previous_escape := true;
        previous_blank := false
    | '['
    | ']'
    | '{'
    | '}'
    | '!'
    | '*'
    | '?'
    | '-' ->
        if !previous_escape then
          (
           arg := !arg ^ "\\";
           previous_escape := false
          );
        arg := !arg ^ (String.make 1 c)
    | ' ' 
    | '\t' ->
        if !previous_escape then
          (
           arg := !arg ^ (String.make 1 c);
           previous_escape := false
          )
        else
          (
           if not !previous_blank then
             (
              argvs := !arg :: (!argvs);
              arg := "";
              previous_blank := true
             )
          )
    | _ ->
        arg := !arg ^ (String.make 1 c);
        previous_blank := false
  done;
  if (String.compare !arg "")<>0 then
    argvs := !arg :: (!argvs);
  List.rev !argvs

(******************************************************************************)

let toplevel () =
  let prompt = "mlftp > " in
  let session = ref default_ftp_session in
  let no_connect = ref true in
  while true do
    print_normal_msg prompt;
    flush_all ();
    let cmd = remove_heading_blank (remove_trailing_cr_lf (read_line ())) in
    print_debug_msg ("---CMD: "^cmd^"\n");
    let args =
      try 
        get_argvs cmd
      with
      | Invalid_argument err_msg ->
          print_error_msg ("Illegal arguments: "^err_msg^"\n");
          [""]
    in
    let cmd_name = List.nth args 0 in
    (
     try
       match cmd_name with
       | "" ->
           ()
       | "bye" ->
           logout !session;
           exit 0
       | "cd" ->
           if !no_connect then
             print_error_msg "No connection.\n"
           else
             cmd_cd !session args
       | "close" ->
           logout !session;
           no_connect := true
       | "exit" ->
           logout !session;
           exit 0
       | "get" ->
           if !no_connect then
             print_error_msg "No connection.\n"
           else
             cmd_get !session args
       | "help" ->
           cmd_help args
       | "lcd" ->
           cmd_lcd args
       | "ls" ->
           if !no_connect then
             print_error_msg "No connection.\n"
           else
             cmd_ls !session args
       | "mget" ->
           if !no_connect then
             print_error_msg "No connection.\n"
           else
             cmd_mget !session args
       | "mkdir" ->
           if !no_connect then
             print_error_msg "No connection.\n"
           else
             cmd_mkdir !session.cc args
       | "mput" ->
           if !no_connect then
             print_error_msg "No connection.\n"
           else
             cmd_mput !session args
       | "nlist" ->
           if !no_connect then
             print_error_msg "No connection.\n"
           else
             cmd_nlist !session args
       | "open" ->
           (
            try
              session := cmd_open args;
              no_connect := false
            with
            | Resolve_failed ->
                print_error_msg "Cannot found the host.\n"
            | Control_connect_failed ->
                print_error_msg "Fail to connect to the host.\n"
            | Login_failed ->
                print_error_msg "Login failed.\n"
           )
       | "put" ->
           if !no_connect then
             print_error_msg "No connection.\n"
           else
             cmd_put !session args
       | "pwd" ->
           if !no_connect then
             print_error_msg "No connection.\n"
           else
             cmd_pwd !session args
       | "quote" ->
           if !no_connect then
             print_error_msg "No connection.\n"
           else
             cmd_quote !session.cc args
       | "rm" ->
           if !no_connect then
             print_error_msg "No connection.\n"
           else
             cmd_rm !session args
       | "set" ->
           cmd_set !session args
       | "site" ->
           if !no_connect then
             print_error_msg "No connection.\n"
           else
             cmd_site !session.cc args
       | "user" ->
           if !no_connect then
             print_error_msg "No connection.\n"
           else
             (
              try
                cmd_user !session args
              with
              | Get_pass_failed ->
                  print_error_msg "User changed failed. Username unchanged.\n"
             )
       | _ ->
           if cmd_name.[0]='!' then
             cmd_shell args
           else
             print_error_msg "Unknown commands.\n"
     with
     | Invalid_argument msg ->
         print_error_msg ("Invalid argument: "^msg^"\n")
     | Data_connect_failed ->
         print_error_msg ("Failed to establish the data connection.\n")
     | Connect_reset ->
         print_error_msg ("Data connection reset by remote.\n")
     | Control_connect_closed ->
         !session.closed <- true;
         no_connect := true
     | Unix_error (errno,s1,s2) ->
         print_error_msg ((error_message errno)^"\n")
     | _ ->
         print_error_msg "Other errors.\n"
    )
  done
